namespace Mapbox.Examples
{
    using UnityEngine;
    using UnityEngine.EventSystems;

    public class CameraMovement : MonoBehaviour
    {
        [SerializeField]
        float _panSpeed = 20f;

        [SerializeField]
        float _zoomSpeed = 50f;

        [SerializeField]
        Camera _referenceCamera;

        Quaternion _originalRotation;
        Vector3 _origin;
        Vector3 _delta;
        bool _shouldDrag;

        void HandleTouch()
        {
            float zoomFactor = 0.0f;
            //pinch to zoom. 
            switch (Input.touchCount)
            {
                case 1:
                    {
                        HandleMouseAndKeyBoard();
                    }
                    break;
                case 2:
                    {
                        // Store both touches.
                        Touch touchZero = Input.GetTouch(0);
                        Touch touchOne = Input.GetTouch(1);

                        // Find the position in the previous frame of each touch.
                        Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
                        Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;

                        // Find the magnitude of the vector (the distance) between the touches in each frame.
                        float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
                        float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;

                        // Find the difference in the distances between each frame.
                        zoomFactor = 0.05f * (touchDeltaMag - prevTouchDeltaMag);
                    }
                    ZoomMapUsingTouchOrMouse(zoomFactor);
                    break;
                default:
                    break;
            }
        }

        void ZoomMapUsingTouchOrMouse(float zoomFactor)
        {
            var y = zoomFactor * _zoomSpeed;
            Vector3 temp = transform.localPosition + transform.forward * y;
            SetZoom(temp);
        }

        void HandleMouseAndKeyBoard()
        {
            var x = 0f;
            var y = 0f;
            var z = 0f;

            if (Input.GetMouseButton(0) && !EventSystem.current.IsPointerOverGameObject())
            {
                var mousePosition = Input.mousePosition;
                mousePosition.z = _referenceCamera.transform.localPosition.y;
                _delta = _referenceCamera.ScreenToWorldPoint(mousePosition) - _referenceCamera.transform.localPosition;
                _delta.y = 0f;
                if (_shouldDrag == false)
                {
                    _shouldDrag = true;
                    _origin = _referenceCamera.ScreenToWorldPoint(mousePosition);
                }
            }
            else
            {
                _shouldDrag = false;
            }

            if (_shouldDrag == true)
            {
                var offset = _origin - _delta;
                offset.y = transform.localPosition.y;
                transform.localPosition = offset;
            }
            else
            {
                if (EventSystem.current.IsPointerOverGameObject())
                {
                    return;
                }

                // TODO: disable keyboard-specific input.
                x = Input.GetAxis("Horizontal");
                z = Input.GetAxis("Vertical");
                y = -Input.GetAxis("Mouse ScrollWheel") * _zoomSpeed;
                Vector3 temp = transform.localPosition + transform.forward * y + (_originalRotation * new Vector3(x * _panSpeed, 0, z * _panSpeed));
                SetZoom(temp);
            }
        }

        private void SetZoom(Vector3 localPos)
        {
            if (localPos.y > 20 && localPos.y <= 380)
            {
                transform.localPosition = localPos;
            }
            transform.localPosition = localPos;
        }

        void Awake()
        {
            _originalRotation = Quaternion.Euler(0, transform.eulerAngles.y, 0);

            if (_referenceCamera == null)
            {
                _referenceCamera = GetComponent<Camera>();
                if (_referenceCamera == null)
                {
                    throw new System.Exception("You must have a reference camera assigned!");
                }
            }
        }

        void LateUpdate()
        {

            if (Input.touchSupported && Input.touchCount > 0)
            {
                HandleTouch();
            }
            else
            {
                HandleMouseAndKeyBoard();
            }
        }

        public void ResetPosition(Vector3 pos)
        {
            transform.position = new Vector3(pos.x, transform.position.y, pos.z);
        }
    }
}